package ru.ipccenter.jshell.frontend.compiler;

import javax.tools.*;
import javax.tools.JavaFileObject.Kind;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class CompilerImpl implements Compiler {

  private final JavaCompiler compiler = 
      ToolProvider.getSystemJavaCompiler();
  private PrintStream errorStream;
  private String classpath;
  
  public CompilerImpl(String classpath, PrintStream errorStream) {
    
    setErrorStream(errorStream);
    setClasspath(classpath);
  }
  
  @Override
  public void setErrorStream(PrintStream errorStream) {

    this.errorStream = errorStream;
  }

  @Override
  public void setClasspath(String classpath) {
    
    this.classpath = classpath;
  }

  @Override
  public Map<String, MemoryByteCode> compileInMemory(String className, String source) {
    
    Map<String, String> sourceMap = 
        Collections.singletonMap(className, source);
    MemoryFileManager manager = 
        (MemoryFileManager) compile(sourceMap, new MemoryFileManager(compiler));
    Map<String, MemoryByteCode> classMap  = 
        manager.getOutputMap();
    
    return classMap;
  }

  @Override
  public ShellFileManager compile(String className, String source) {
    
    Map<String, String> sourceMap = 
        Collections.singletonMap(className, source);
    return compile(sourceMap, new TmpClasspathFileManager(compiler, classpath, errorStream));
  }
  
  
  protected ShellFileManager compile(Map<String, String> sources,
      ShellFileManager fileManager) {
    
    List<MemorySource> list = new ArrayList<MemorySource>();
    for (Map.Entry<String, String> entry : sources.entrySet()) {
      list.add(new MemorySource(entry.getKey(), Kind.SOURCE, entry.getValue()));
    }

    DiagnosticCollector<JavaFileObject> diagnostics =
        new DiagnosticCollector<JavaFileObject>();
    
    this.compiler.getTask(null, fileManager, diagnostics, null, null, list).call();
    
    for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
      errorStream.format(
          "Error on line %d in %s\n\n", 
          diagnostic.getLineNumber(), 
          diagnostic.toString());
    }   
    
    return fileManager;
  }
}
